import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:signage/app/constants/template_constants.dart';
import 'package:signage/app/data/models/board_element.dart';
import 'package:signage/app/data/models/member_info_item_model.dart';
import 'dart:ui' as ui;
import 'dart:io';
import 'package:signage/native_bridge.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
import 'package:flutter/services.dart' show rootBundle;
import 'package:signage/utils/eink_image_processor.dart';

class TemplateModel {
  final String id;
  String name;
  String category;
  final String categoryType;
  final String filePath;
  final bool isSystemTemplate;
  bool isPinned;
  int backgroundColor;
  String? backgroundImagePath;
  List<MemberInfoItem> memberInfos;
  List<Map<String, dynamic>> elements;
  final int width;
  final int height;
  final String size;
  TemplateModel({
    required this.id,
    required this.name,
    required this.category,
    required this.categoryType,
    required this.filePath,
    this.isSystemTemplate = false,
    this.isPinned = false,
    this.backgroundColor = 0xFFFFFFFF,
    this.backgroundImagePath,
    this.memberInfos = const [],
    this.elements = const [],
    this.width = 800,
    this.height = 480,
    this.size = TemplateConstants.defaultTemplateSize,
  });

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'category': category,
      'categoryType': categoryType,
      'filePath': filePath,
      'isSystemTemplate': isSystemTemplate,
      'isPinned': isPinned,
      'backgroundColor': backgroundColor,
      'backgroundImagePath': backgroundImagePath,
      'memberInfos': memberInfos.map((member) => member.toJson()).toList(),
      'elements': elements,
      'width': width,
      'height': height,
      'size': size,
    };
  }

  factory TemplateModel.fromJson(Map<String, dynamic> json) {
    return TemplateModel(
      id: json['id'] ?? '',
      name: json['name'] ?? '',
      category: json['category'] ?? '',
      categoryType: json['categoryType'] ?? '',
      filePath: json['filePath'] ?? '',
      isSystemTemplate: json['isSystemTemplate'] ?? false,
      isPinned: json['isPinned'] ?? false,
      backgroundColor: json['backgroundColor'] ?? 0xFFFFFFFF,
      backgroundImagePath: json['backgroundImagePath'],
      memberInfos: (json['memberInfos'] as List<dynamic>?)
              ?.map((e) => MemberInfoItem.fromJson(e as Map<String, dynamic>))
              .toList() ??
          [],
      elements:
          (json['elements'] as List<dynamic>?)?.cast<Map<String, dynamic>>() ??
              [],
      width: json['width'] ?? 800,
      height: json['height'] ?? 480,
      size: json['size'] ?? TemplateConstants.defaultTemplateSize,
    );
  }

  void setElementsFromBoardElements(List<BoardElement> boardElements) {
    elements = boardElements.map((element) {
      Map<String, dynamic> elementData;

      if (element is BackgroundBoardElement) {
        elementData = {
          'type': 'background',
          'id': element.id,
          'position': {
            'dx': element.position.value.dx,
            'dy': element.position.value.dy
          },
          'size': {
            'width': element.size.value.width,
            'height': element.size.value.height
          },
          'backgroundColor': element.backgroundColor.value.value,
          'backgroundImagePath': element.backgroundImagePath.value,
          'initialBackgroundImagePath':
              element.initialBackgroundImagePath.value,
          'processMode': element.processMode.value.toString(),
        };
      } else if (element is ImageBoardElement) {
        elementData = {
          'type': 'image',
          'id': element.id,
          'position': {
            'dx': element.position.value.dx,
            'dy': element.position.value.dy
          },
          'size': {
            'width': element.size.value.width,
            'height': element.size.value.height
          },
          'imagePath': element.imagePath.value,
          'initialImagePath': element.initialImagePath.value,
          'processMode': element.processMode.value.toString(),
          'isFit': element.isFit.value,
        };
      } else if (element is TextBoardElement) {
        elementData = {
          'type': 'text',
          'id': element.id,
          'position': {
            'dx': element.position.value.dx,
            'dy': element.position.value.dy
          },
          'size': {
            'width': element.size.value.width,
            'height': element.size.value.height
          },
          'fontFamily': element.fontFamily.value,
          'text': element.text.value,
          'fontSize': element.fontSize.value,
          'color': element.color.value.value,
          'isScalable': element.isScalable.value,
          'isLocked': element.isLocked.value,
          'textAlign': element.textAlign.value.toString(),
        };
      } else if (element is ColorBlockBoardElement) {
        elementData = {
          'type': 'colorBlock',
          'id': element.id,
          'position': {
            'dx': element.position.value.dx,
            'dy': element.position.value.dy
          },
          'size': {
            'width': element.size.value.width,
            'height': element.size.value.height
          },
          'color': element.color.value.value,
        };
      } else {
        elementData = {'type': 'unknown'};
      }

      return elementData;
    }).toList();
  }

  RxList<BoardElement> toBoardElements(
      {double scale = 1.0, double? maxHeight}) {
    return RxList<BoardElement>(
        elements.map<BoardElement>((Map<String, dynamic> element) {
      switch (element['type'] as String) {
        case 'background':
          final backgroundBoardElement = BackgroundBoardElement(
            id: element['id'],
            initialPosition: Offset(
              element['position']['dx'] * scale,
              element['position']['dy'] * scale,
            ),
            initialSize: Size(
              element['size']['width'] * scale,
              (element['size']['height'] * scale > maxHeight)
                  ? maxHeight
                  : element['size']['height'] * scale,
            ),
            initialColor: Color(element['backgroundColor']),
          );
          if (element['backgroundImagePath'] != null) {
            backgroundBoardElement.backgroundImagePath.value =
                element['backgroundImagePath'] ?? '';
            backgroundBoardElement.initialBackgroundImagePath.value =
                element['initialBackgroundImagePath'] ?? '';
            backgroundBoardElement.processMode.value =
                ProcessMode.values.firstWhere(
              (e) => e.toString() == element['processMode'],
              orElse: () => ProcessMode.none,
            );
          }
          return backgroundBoardElement;
        case 'image':
          return ImageBoardElement(
            id: element['id'],
            initialPosition: Offset(
              element['position']['dx'] * scale,
              element['position']['dy'] * scale,
            ),
            initialSize: Size(
              element['size']['width'] * scale,
              (element['size']['height'] * scale > maxHeight)
                  ? maxHeight
                  : element['size']['height'] * scale,
            ),
          )
            ..processMode.value = ProcessMode.values.firstWhere(
              (e) => e.toString() == element['processMode'],
              orElse: () => ProcessMode.none,
            )
            ..imagePath.value = element['imagePath']
            ..initialImagePath.value = element['initialImagePath']
            ..isFit.value = element['isFit'] ?? false;

        case 'text':
          return TextBoardElement(
            id: element['id'],
            initialPosition: Offset(
              element['position']['dx'] * scale,
              element['position']['dy'] * scale,
            ),
            initialSize: Size(
              element['size']['width'] * scale,
              (element['size']['height'] * scale > maxHeight)
                  ? maxHeight
                  : element['size']['height'] * scale,
            ),
            initialText: element['text'],
            initialIsScalable: element['isScalable'] ?? true,
            initialIsLocked: element['isLocked'] ?? false,
            initialFontFamily: element['fontFamily'],
            initialFontSize: (element['fontSize'] ?? 16.0) * scale,
            initialColor: Color(element['color'] ?? Colors.black.value),
            initialTextAlign: element['textAlign'] != null
                ? TextAlign.values.firstWhere(
                    (e) => e.toString() == element['textAlign'],
                    orElse: () => TextAlign.center,
                  )
                : TextAlign.center,
          );

        case 'colorBlock':
          return ColorBlockBoardElement(
            id: element['id'],
            initialPosition: Offset(
              element['position']['dx'] * scale,
              element['position']['dy'] * scale,
            ),
            initialSize: Size(
              element['size']['width'] * scale,
              (element['size']['height'] * scale > maxHeight)
                  ? maxHeight
                  : element['size']['height'] * scale,
            ),
            initialColor: Color(element['color']),
          );

        default:
          throw Exception('Unknown element type: ${element['type']}');
      }
    }).toList());
  }

  void updateText(String id, String newText) {
    final index = elements.indexWhere(
        (element) => element['type'] == 'text' && element['id'] == id);

    if (index != -1) {
      elements[index]['text'] = newText;
    }
  }

  List<TextBoardElement> getAllTextElements() {
    return elements
        .where((element) => element['type'] == 'text')
        .map((element) => TextBoardElement(
              id: element['id'],
              initialPosition: Offset(
                element['position']['dx'],
                element['position']['dy'],
              ),
              initialSize: Size(
                element['size']['width'],
                element['size']['height'],
              ),
              initialText: element['text'],
              initialIsScalable: element['isScalable'] ?? true,
              initialIsLocked: element['isLocked'] ?? false,
              initialFontFamily: element['fontFamily'],
              initialFontSize: element['fontSize'] ?? 16.0,
              initialColor: Color(element['color'] ?? Color(0xFF000000).value),
              initialTextAlign: element['textAlign'] != null
                  ? TextAlign.values.firstWhere(
                      (e) => e.toString() == element['textAlign'],
                      orElse: () => TextAlign.center,
                    )
                  : TextAlign.center,
            ))
        .toList();
  }

  Future<String?> convertToEpaper(
    String fileName,
    int displayType, {
    int width = 800,
    int height = 480,
    bool isClear = false,
    List<String>? textList,
  }) async {
    try {
      if (isClear) {
        fileName = 'clear.bin';
      }
      final directory = await getTemporaryDirectory();
      final targetPath = path.join(directory.path, 'epaper', fileName);
      final imageDataPath = path.join(directory.path, 'epaper',
          '${path.basenameWithoutExtension(fileName)}_data.png');

      // Add debug information

      // Ensure directory exists
      final targetDir = Directory(path.dirname(targetPath));
      if (!await targetDir.exists()) {
        await targetDir.create(recursive: true);
      }

      // 从 background element 获取原始尺寸
      final backgroundElement = elements.firstWhere(
        (element) => element['type'] == 'background',
        orElse: () => {
          'size': {'width': width.toDouble(), 'height': height.toDouble()}
        },
      );
      final originalWidth = backgroundElement['size']['width'].toDouble();

      // 计算缩放比例
      final scaleX = width / originalWidth;
      final scale = scaleX; // 使用统一的缩放比例以保持宽高比

      // 创建画布（使用目标尺寸）
      final recorder = ui.PictureRecorder();
      final canvas = Canvas(recorder);
      final paint = Paint();

      if (!isClear) {
        // 获取所有文本元素
        final textElements =
            elements.where((element) => element['type'] == 'text').toList();

        var textContentIndex = 0;
        // 绘制所有元素时应用缩放
        for (final element in elements) {
          final position = Offset(
            element['position']['dx'] * scale,
            element['position']['dy'] * scale,
          );
          final size = Size(
            element['size']['width'] * scale,
            element['size']['height'] * scale,
          );

          switch (element['type']) {
            case 'text':
              final textIndex = textElements.indexOf(element);
              var text = element['text'];
              if (!(element['isLocked'] ?? false)) {
                text = (textList != null && textContentIndex < textList.length)
                    ? textList[textContentIndex]
                    : (textList != null ? '' : element['text']);
                textContentIndex++;
              }
              if (text.isNotEmpty) {
                // 获取文本对齐方式
                final textAlign = element['textAlign'] != null
                    ? TextAlign.values.firstWhere(
                        (e) => e.toString() == element['textAlign'],
                        orElse: () => TextAlign.center,
                      )
                    : TextAlign.center;

                final textPainter = TextPainter(
                  text: TextSpan(
                    text: text,
                    style: TextStyle(
                      fontSize:
                          (element['fontSize']?.toDouble() ?? 16.0) * scale,
                      fontFamily: element['fontFamily'],
                      color: Color(element['color'] ?? Color(0xFF000000).value),
                    ),
                  ),
                  textDirection: TextDirection.ltr,
                  textAlign: textAlign,
                )..layout();

                // 根据对齐方式计算x坐标
                double dx;
                switch (textAlign) {
                  case TextAlign.left:
                    dx = position.dx;
                    break;
                  case TextAlign.right:
                    dx = position.dx + size.width - textPainter.width;
                    break;
                  case TextAlign.center:
                  default:
                    dx = position.dx + (size.width - textPainter.width) / 2;
                    break;
                }

                // 垂直方向保持居中
                final double dy =
                    position.dy + (size.height - textPainter.height) / 2;

                // 添加调试信息
                print('Text: $text');
                print('TextAlign: $textAlign');
                print('Position: ($dx, $dy)');
                print('Text size: ${textPainter.size}');

                textPainter.paint(canvas, Offset(dx, dy));
              }
              break;

            case 'background':
              if (element['backgroundImagePath'] != null &&
                  element['backgroundImagePath'].isNotEmpty) {
                print(
                    'backgroundImageBytes: ${element['backgroundImagePath']}');
                Uint8List imageBytes;
                if (element['backgroundImagePath'].startsWith('assets/')) {
                  imageBytes = await rootBundle
                      .load(element['backgroundImagePath'])
                      .then((value) => value.buffer.asUint8List());
                } else {
                  imageBytes =
                      await File(element['backgroundImagePath']).readAsBytes();
                }
                final codec = await ui.instantiateImageCodec(imageBytes);
                final frame = await codec.getNextFrame();
                canvas.drawImageRect(
                  frame.image,
                  Rect.fromLTWH(0, 0, frame.image.width.toDouble(),
                      frame.image.height.toDouble()),
                  Rect.fromLTWH(
                      position.dx, position.dy, size.width, size.height),
                  paint,
                );
              } else {
                canvas.drawRect(
                  Rect.fromLTWH(
                      position.dx, position.dy, size.width, size.height),
                  Paint()..color = Color(element['backgroundColor']),
                );
              }
              break;

            case 'image':
              try {
                print('[IMAGE_DRAW] Start drawing image element');
                Uint8List imageBytes;
                if (element['imagePath'].startsWith('assets/')) {
                  imageBytes = await rootBundle
                      .load(element['imagePath'])
                      .then((value) => value.buffer.asUint8List());
                } else {
                  imageBytes = await File(element['imagePath']).readAsBytes();
                }
                final codec = await ui.instantiateImageCodec(imageBytes);
                final frame = await codec.getNextFrame();
                final bool isFit = element['isFit'] ?? false;
                print('[IMAGE_DRAW] isFit: $isFit');
                print(
                    '[IMAGE_DRAW] Container size: ${size.width}x${size.height}');
                final srcRect = Rect.fromLTWH(
                  0,
                  0,
                  frame.image.width.toDouble(),
                  frame.image.height.toDouble(),
                );

                Rect dstRect;
                if (!isFit) {
                  // 如果不是fit模式，计算保持原始比例的尺寸
                  final double imageRatio =
                      frame.image.width / frame.image.height;
                  final double containerRatio = size.width / size.height;

                  double drawWidth = size.width;
                  double drawHeight = size.height;
                  double offsetX = position.dx;
                  double offsetY = position.dy;

                  if (imageRatio > containerRatio) {
                    // 图片比容器更宽
                    drawWidth = size.width;
                    drawHeight = drawWidth / imageRatio;
                    offsetY = position.dy + (size.height - drawHeight) / 2;
                  } else {
                    // 图片比容器更高
                    drawHeight = size.height;
                    drawWidth = drawHeight * imageRatio;
                    offsetX = position.dx + (size.width - drawWidth) / 2;
                  }

                  dstRect =
                      Rect.fromLTWH(offsetX, offsetY, drawWidth, drawHeight);
                } else {
                  // fit模式直接拉伸填充
                  dstRect = Rect.fromLTWH(
                    position.dx,
                    position.dy,
                    size.width,
                    size.height,
                  );
                }

                print('[IMAGE_DRAW] Source rect: $srcRect');
                print('[IMAGE_DRAW] Destination rect: $dstRect');

                canvas.drawImageRect(
                  frame.image,
                  srcRect,
                  dstRect,
                  paint,
                );
              } catch (e, stackTrace) {
                print('[IMAGE_DRAW] Error drawing image: $e');
                print('[IMAGE_DRAW] Stack trace: $stackTrace');
              }
              break;

            case 'colorBlock':
              canvas.drawRect(
                Rect.fromLTWH(
                    position.dx, position.dy, size.width, size.height),
                Paint()..color = Color(element['color']),
              );
              break;
          }
        }
        // Save the original image data
      } else {
        canvas.drawRect(
          Rect.fromLTWH(0, 0, width.toDouble(), height.toDouble()),
          Paint()..color = Color(0xFFFFFFFF),
        );
      }

      final picture = recorder.endRecording();
      final ui.Image finalImage = await picture.toImage(width, height);

      // Save the PNG image data
      final pngBytes =
          await finalImage.toByteData(format: ui.ImageByteFormat.png);
      if (pngBytes != null) {
        await File(imageDataPath).writeAsBytes(pngBytes.buffer.asUint8List());
      }

      // Convert to e-paper format
      final ByteData? byteData = await finalImage.toByteData(
        format: ui.ImageByteFormat.rawRgba,
      );

      if (byteData == null) {
        print('无法获取图像数据');
        return null;
      }

      final result = await convertImageToEpaper(
        byteData,
        width,
        height,
        displayType,
        targetPath,
      );
      print('convertImageToEpaper result: $result');
      if (result != 0) {
        print('Failed to convert image. Error code: $result');
        return null;
      }

      return targetPath;
    } catch (e, stackTrace) {
      print('转换失败: $e');
      print('Stack trace: $stackTrace');
      return null;
    }
  }
}
